package com.ydl.iec.transport;

import com.ydl.iec.iec104.core.Iec104ThreadLocal;
import com.ydl.iec.iec104.enums.TypeIdentifierEnum;
import com.ydl.iec.util.ByteUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

public class FileFrameHandler extends ChannelInboundHandlerAdapter {
    private static final Logger LOGGER = LoggerFactory.getLogger(FileFrameHandler.class);
    private String workDir = "\\model";
    private HashMap<Integer,String> bufferFile = new HashMap<>(10);
    /**
     * 拦截系统消息
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf result = (ByteBuf) msg;
        byte[] bytes = new byte[result.readableBytes()];
        result.readBytes(bytes);
        if (bytes[6] == TypeIdentifierEnum.directoryCall.getValue()) { //收到文件召唤
            Iec104ThreadLocal.getControlPool().increaseAccept();//设置接收变量
            FileFrame fileFrame = FileFrameFactory.decodeToFileFrame(bytes); //转换成可处理的对象
            AttachData attachObject = fileFrame.getAttach();
            AttachTypeEnum type = AttachTypeEnum.valOf(fileFrame.getOp()); //操作标识
            switch (type){
                case DIRCFM:
                    //回复确认消息
                    break;
                case DIRCALL:
                    LOGGER.info("文件目录召唤.");
                    handlerDirCall(ctx, fileFrame,(AttachDataCallDir)attachObject); //处理目录召唤请求,应答召唤
                    break;
                case FILECALL:
                    AttachDataCallFile file = (AttachDataCallFile)attachObject;
                    LOGGER.info("文件召唤激活. {} - {}", file.getId(),file.getFileName());
                    handlerFileCall(ctx, fileFrame,file); //处理目录召唤请求,应答召唤
                    //应答后主站没有反应，直接传输文件
                    // 传输文件
//                    file.setId(1);
                    handlerFileTrans(ctx, fileFrame,file);
                    break;
                case FILETRANCFM:
                    LOGGER.info("文件传输确认.");
                    handlerFileTransConfirm(ctx, fileFrame,(AttachDataCallTrans)attachObject);
                    break;
            }
        }else{
            result.writeBytes(bytes);
            ctx.fireChannelRead(result);
        }
    }

    /**
     * 目录召唤
     * @param callDir 召唤指令
     */
    private void handlerDirCall(ChannelHandlerContext ctx,FileFrame fileFrame,AttachDataCallDir callDir){
        //TODO 目前没有实现默认目录的文件读取
        AttachDataReplyDir reply = new AttachDataReplyDir();//响应对象
//        this.workDir = callDir.getName();
        LOGGER.info("获取目录 {} 下的文件,并响应召唤.",this.workDir);
        File fileDir = new File(this.workDir);
        FileFilter filter = null;
        if(!fileDir.exists()){
            //目录不存在,回复错误
            reply.setSuccess(1);
        }else if(callDir.getFlag() == 0){
            //全部文件
            reply.setSuccess(0);
            filter = File::isFile;
        }else{
            //指定之间段内的文件
            reply.setSuccess(0);
            filter = (file)->{
                boolean ret = false;
                if(file.isFile()){
                    long modifyTime = file.lastModified();
                    long beginTime = callDir.getStartTime().getTime();
                    long endTime = callDir.getEndTime().getTime();
                    ret = beginTime <= modifyTime && endTime >= modifyTime;
                }
                return ret;
            };
        }
        fileFrame.setVsq((byte)1);
        fileFrame.setCot((short)5);
        fileFrame.setOp((byte)2);
        reply.setId(callDir.getId());
        if(filter == null) {
            reply.setSuccess(1);
            reply.setMore(0);
        }else{
            File[] arrayFile = fileDir.listFiles(filter);
            List<AttachFile> lstFile = new ArrayList<>(arrayFile.length);
            reply.setAmount(arrayFile.length);
            for (File f : arrayFile) {
                AttachFile af = new AttachFile();
                af.setFileName(f.getName());
                af.setAttribute(0);//文件属性备用
                af.setSize((int)f.length());
                af.setCp56time(ByteUtil.date2Hbyte(new Date(f.lastModified())));
                lstFile.add(af);
            }
            reply.setAttachFiles(lstFile);
        }
        fileFrame.setAttach(reply);
        List<byte[]> dataList = FileFrameFactory.encodeFromFileFrame(fileFrame);
        for(byte[] d : dataList){
            ctx.channel().writeAndFlush(d);
        }
    }

    private void handlerFileCall(ChannelHandlerContext ctx,FileFrame fileFrame,AttachDataCallFile callFile){
        fileFrame.setVsq((byte)1);
        fileFrame.setCot((short)7);
        String fileName = callFile.getFileName();
        bufferFile.put(callFile.getId(),fileName);
        if(fileName != null){
            File f = new File(workDir,fileName);
            AttachDataReplyFile file = new AttachDataReplyFile();
            file.setId(callFile.getId());
            //应答读文件激活
            if(f.exists()){
                file.setSuccess(0);
                file.setFileName(f.getName());
                file.setSize((int)f.length());
                System.out.println(f.getAbsolutePath());
            }else{
                file.setSuccess(1);
                file.setSize(0);
            }
            fileFrame.setOp((byte)4);
            fileFrame.setAttach(file);
            List<byte[]> dataList = FileFrameFactory.encodeFromFileFrame(fileFrame);
            for(byte[] d : dataList){
                ctx.channel().writeAndFlush(d);
            }
        }
    }

    private void handlerFileTrans(ChannelHandlerContext ctx,FileFrame fileFrame,AttachDataCallFile callFile){
        uploadFile(ctx,fileFrame,callFile.getId(),callFile.getFileName(),0);
    }
    private void handlerFileTransConfirm(ChannelHandlerContext ctx,FileFrame fileFrame,AttachDataCallTrans callFile){
        LOGGER.info("文件段号：{}",callFile.getSegment());
        if(callFile.getMore() == 1){
            LOGGER.info("继续发：{}",callFile.getId());
            Integer fileId = callFile.getId();
            String fileName = bufferFile.get(fileId);
            uploadFile(ctx,fileFrame,fileId,fileName,callFile.getSegment());
        }
    }

    private void uploadFile(ChannelHandlerContext ctx,FileFrame fileFrame,Integer fileId,String fileName,int segment){
        fileFrame.setVsq((byte)1);
        fileFrame.setCot((short)5);
        if(fileName != null){
            File f = new File(workDir,fileName);
            AttachDataReplyTrans file = new AttachDataReplyTrans();
            file.setId(fileId);
            file.setSegment(segment);
            //应答读文件激活
            if(f.exists()){
                file.setTransFile(f);
            }
            fileFrame.setOp((byte)5);
            fileFrame.setAttach(file);
            List<byte[]> dataList = FileFrameFactory.encodeFromFileFrame(fileFrame);
            for(byte[] d : dataList){
                LOGGER.info("文件内容：{}",ByteUtil.byteArray2HexString(d,d.length,true));
                ctx.channel().writeAndFlush(d);
            }
        }
    }
}
